package step

import (
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	mpccrypto "github.com/wanchain/go-mpc/storeman/storemanmpc/crypto"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"sync"
)

type RandomPolynomialValue struct {
	randCoefficient []big.Int //coefficient
	//message         map[uint64]big.Int //Polynomil result
	message     map[discover.NodeID]big.Int //Polynomil result
	polyValue   []big.Int
	result      *big.Int
	bJRSS       bool
	smcer       mpcprotocol.SchnorrMPCer
	ContextId   uint64
	grpIdString string
	wrLock      sync.RWMutex
	BaseGenerator
}

func createJRSSValue(degree int, peerNum int) *RandomPolynomialValue {
	return &RandomPolynomialValue{
		make([]big.Int, degree+1),
		make(map[discover.NodeID]big.Int),
		make([]big.Int, peerNum),
		nil,
		true,
		nil,
		0,
		"",
		sync.RWMutex{},
		BaseGenerator{}}
}

func createJZSSValue(degree int, peerNum int) *RandomPolynomialValue {
	return &RandomPolynomialValue{
		make([]big.Int, degree+1),
		make(map[discover.NodeID]big.Int),
		make([]big.Int, peerNum),
		nil,
		false,
		nil,
		0,
		"",
		sync.RWMutex{},
		BaseGenerator{}}
}

func (poly *RandomPolynomialValue) initialize(totalNum int, result mpcprotocol.MpcResultInterface) error {

	// get randCoefficient
	_, grpIdString, _ := osmconf.GetGrpId(result)
	poly.grpIdString = grpIdString

	cof, err := mpccrypto.GetRandCoefficients(len(poly.randCoefficient))
	if err != nil {
		log.SyslogErr("RandomPolynomialValue, GetRandCoefficients fail. err:%s", err.Error())
		return err
	}

	copy(poly.randCoefficient, cof)
	if !poly.bJRSS {
		poly.randCoefficient[0] = *big.NewInt(0)
	}

	curveTypeBytes, err := result.GetByteValue(mpcprotocol.MpcCurve)
	if err != nil {
		return err
	}
	curveType := big.NewInt(0).SetBytes(curveTypeBytes).Int64()
	log.SyslogDebug("RandomPolynomialValue::initialize", "curveType", curveType, "ctx", poly.GetContextId())
	smpcer := osmconf.GetSmpcer(int(curveType))

	for i := 0; i < len(poly.polyValue); i++ {

		//nodeId := &(*peers)[i].PeerID
		nodeId, _ := osmconf.GetOsmConf().GetNodeIdByIndex(grpIdString, uint16(i))
		xValue, err := osmconf.GetOsmConf().GetXValueByNodeId(grpIdString, nodeId, smpcer)
		if err != nil {
			log.SyslogErr("RandomPolynomialValue", "initialize.GetXValueByNodeId", err.Error(), "ctx", poly.GetContextId())
		}
		poly.polyValue[i] = mpccrypto.EvaluatePoly(poly.randCoefficient, xValue)
	}

	return nil
}

func (poly *RandomPolynomialValue) calculateResult() error {
	poly.result = big.NewInt(0)
	var nodeId []*discover.NodeID
	var indexes []big.Int

	defer poly.wrLock.RUnlock()
	poly.wrLock.RLock()

	for id, value := range poly.message {
		nodeIdBytes := id
		nodeId = append(nodeId, &nodeIdBytes)
		poly.result.Add(poly.result, &value)
		poly.result.Mod(poly.result, crypto.Secp256k1_N)
		index, _ := osmconf.GetOsmConf().GetInxByNodeId(poly.grpIdString, &id)
		indexBig := big.NewInt(0).SetUint64(uint64(index))
		indexes = append(indexes, *indexBig)
	}
	bigCollection, _ := osmconf.BuildDataByIndexes(&indexes)
	log.SyslogInfo("btc_jrss_generator", "len(peer)", len(nodeId),
		"bigcollection", hexutil.Encode(bigCollection.Bytes()),
		"calculateResult peers", discover.ArrNodeIdpsToStr(nodeId),
		"ctx", poly.GetContextId())
	return nil
}

func (poly *RandomPolynomialValue) SetSchnorrMpcer(smcer mpcprotocol.SchnorrMPCer) error {
	poly.smcer = smcer
	return nil
}

func (poly *RandomPolynomialValue) SetContextId(contextId uint64) {
	poly.ContextId = contextId
}

func (poly *RandomPolynomialValue) GetContextId() uint64 {
	return poly.ContextId
}

func (poly *RandomPolynomialValue) SetMpcContext(mpc MpcContextFunc) {
	poly.mpc = mpc
}

func (poly *RandomPolynomialValue) GetMpcContext() MpcContextFunc {
	return poly.mpc
}
